<!Doctype html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<title>Python 函数装饰器 | 菜鸟教程</title>

  <meta name='robots' content='max-image-preview:large' />
<link rel='stylesheet' id='classic-theme-styles-css' href='../0/classic-themes.min.css?ver=6.2' type='text/css' media='all' />
<link rel="canonical" href="../w3cnote/python-func-decorators.html" />
<meta name="keywords" content="Python 函数装饰器">
<meta name="description" content="装饰器(Decorators)是 Python 的一个重要部分。简单地说：他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短，也更Pythonic（Python范儿）。大多数初学者不知道在哪儿使用它们，所以我将要分享下，哪些区域里装饰器可以让你的代码更简洁。 首先，让我们讨论下如何写你自己的装饰器。 这可能是最难掌握的概念之一。我们会每次只讨论一个步骤，这样你能完全理解它。  一切皆对象 首先我们来理解下 Python 中的函..">
		
	<link rel="shortcut icon" href="https://static.runoob.com/images/favicon.ico">
	<link rel="stylesheet" href="../0/style.css?v=1.170" type="text/css" media="all" />	
	<link rel="stylesheet" href="../0/font-awesome.min.css" media="all" />	
  <!--[if gte IE 9]><!-->
  <script src=""></script>
  <!--<![endif]-->
  <!--[if lt IE 9]>
     <script src=""></script>
     <script src=""></script>
  <![endif]-->
  <link rel="apple-touch-icon" href="https://static.runoob.com/images/icon/mobile-icon.png"/>
  <meta name="apple-mobile-web-app-title" content="菜鸟教程">
</head>
<body>

<!--  头部 -->
<div class="container logo-search">

  <div class="col search row-search-mobile">
    <form action="index.php">
      <input class="placeholder" placeholder="搜索……" name="s" autocomplete="off">
      
    </form>
  </div>

  <div class="row">
    <div class="col logo">
      <h1><a href="../">菜鸟教程 -- 学的不仅是技术，更是梦想！</a></h1>
    </div>
        <div class="col right-list"> 
    <button class="btn btn-responsive-nav btn-inverse" data-toggle="collapse" data-target=".nav-main-collapse" id="pull" style=""> <i class="fa fa-navicon"></i> </button>
    </div>
        
    <div class="col search search-desktop last">
      <div class="search-input" >
      <form action="//www.runoob.com/" target="_blank">
        <input class="placeholder" id="s" name="s" placeholder="搜索……"  autocomplete="off" style="height: 44px;">
      </form>
      
      </div>
    </div>
  </div>
</div>



<!-- 导航栏 -->
<div class="container navigation">
    <div class="row">
        <div class="col nav">
            

                        <ul class="pc-nav" id="note-nav">
                <li><a href="../">首页</a></li>
                <li><a href="../w3cnote">笔记首页</a></li>
                <li><a href="../w3cnote/android-tutorial-intro.html" title="Android 基础入门教程">Android</a></li>
                <li><a href="../w3cnote/es6-tutorial.html" title="ES6 教程">ES6 教程</a></li>
                <li><a href="../w3cnote/ten-sorting-algorithm.html" title="排序算法">排序算法</a></li>
                <li><a href="../w3cnote/hadoop-tutorial.html" title="Hadoop 教程">Hadoop</a></li>
                <li><a href="../w3cnote/zookeeper-tutorial.html" title="Zookeeper 教程">Zookeeper</a></li>
                <li><a href="../w3cnote/verilog-tutorial.html" title="Verilog 教程">Verilog</a></li>
                <li><a href="../w3cnote_genre/code" title="编程技术">编程技术</a></li> 
                <li><a href="../w3cnote_genre/coderlife" title="程序员人生">程序员人生</a></li>
                
                <!--<li><a href="javascript:;" class="runoob-pop">登录</a></li>
                
                
                        <li>
                <a style="font-weight:bold;" href="../linux/linux-tutorial.html#yunserver" target="_blank" onclick="_hmt.push(['_trackEvent', 'aliyun', 'click', 'aliyun'])" title="kkb">云服务器</a>
                </li>
                <li><a href="http://gk.link/a/104mQ" target="_blank" style="font-weight: bold;"onclick="_hmt.push(['_trackEvent', '极客时间', 'click', 'jike'])" title="我的圈子">极客时间</a></li>
            
                
                <li><a target="_blank" href="../shoppinglist" rel="nofollow">知识店铺</a></li> 
        -->
            </ul>
                        
              
            <ul class="mobile-nav">
                <li><a href="../w3cnote">首页</a></li>
                <li><a href="../w3cnote_genre/android" target="_blank" title="Android 基础入门教程">Android</a></li>
                <li><a href="../w3cnote/es6-tutorial.html" target="_blank" title="ES6 教程">ES6</a></li>
                <li><a href="../w3cnote_genre/joke" target="_blank" title="程序员笑话">逗乐</a></li>
                
                <a href="javascript:void(0)" class="search-reveal">Search</a> 
            </ul>
            
        </div>
    </div>
</div>


<!--  内容  -->
<div class="container main">
	<div class="row">

		<div class="col middle-column big-middle-column">
	 			<div class="article">
			<div class="article-heading">
				<h2>Python 函数装饰器</h2>				<h3><em>分类</em> <a href="../w3cnote_genre/code" title="编程技术" >编程技术</a> </h3>
			</div>
			<div class="article-body note-body">
				<div class="article-intro">
					<p>装饰器(Decorators)是 Python 的一个重要部分。简单地说：他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短，也更Pythonic（Python范儿）。大多数初学者不知道在哪儿使用它们，所以我将要分享下，哪些区域里装饰器可以让你的代码更简洁。
首先，让我们讨论下如何写你自己的装饰器。</p>
<p>这可能是最难掌握的概念之一。我们会每次只讨论一个步骤，这样你能完全理解它。</p>
<h3>
一切皆对象</h3>
<p>首先我们来理解下 Python 中的函数:</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">yasoob</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">hi </span><span class="hl-quotes">&quot;</span><span class="hl-code"> + </span><span class="hl-identifier">name</span><span class="hl-code">
 
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># output: 'hi yasoob'</span><span class="hl-code">
 
</span><span class="hl-comment"># 我们甚至可以将一个函数赋值给一个变量，比如</span><span class="hl-code">
</span><span class="hl-identifier">greet</span><span class="hl-code"> = </span><span class="hl-identifier">hi</span><span class="hl-code">
</span><span class="hl-comment"># 我们这里没有在使用小括号，因为我们并不是在调用hi函数</span><span class="hl-code">
</span><span class="hl-comment"># 而是在将它放在greet变量里头。我们尝试运行下这个</span><span class="hl-code">
 
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">greet</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># output: 'hi yasoob'</span><span class="hl-code">
 
</span><span class="hl-comment"># 如果我们删掉旧的hi函数，看看会发生什么！</span><span class="hl-code">
</span><span class="hl-reserved">del</span><span class="hl-code"> </span><span class="hl-identifier">hi</span><span class="hl-code">
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: NameError</span><span class="hl-code">
 
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">greet</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: 'hi yasoob'</span></div>
</div>
</div>
<h3>在函数中定义函数</h3>
<p>刚才那些就是函数的基本知识了。我们来让你的知识更进一步。在 Python 中我们可以在一个函数中定义另一个函数：</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">yasoob</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">now you are inside the hi() function</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">greet</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">now you are in the greet() function</span><span class="hl-quotes">&quot;</span><span class="hl-code">
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">welcome</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">now you are in the welcome() function</span><span class="hl-quotes">&quot;</span><span class="hl-code">
 
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">greet</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">welcome</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">now you are back in the hi() function</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
</span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#output:now you are inside the hi() function</span><span class="hl-code">
</span><span class="hl-comment">#       now you are in the greet() function</span><span class="hl-code">
</span><span class="hl-comment">#       now you are in the welcome() function</span><span class="hl-code">
</span><span class="hl-comment">#       now you are back in the hi() function</span><span class="hl-code">
 
</span><span class="hl-comment"># 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。</span><span class="hl-code">
</span><span class="hl-comment"># 然后greet()和welcome()函数在hi()函数之外是不能访问的，比如：</span><span class="hl-code">
 
</span><span class="hl-identifier">greet</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: NameError: name 'greet' is not defined</span></div>
</div>
</div>
<p>那现在我们知道了可以在函数中定义另外的函数。也就是说：我们可以创建嵌套的函数。现在你需要再多学一点，就是函数也能返回函数。</p>
<h3>
从函数中返回函数</h3>
<p>其实并不需要在一个函数里去执行另一个函数，我们也可以将其作为输出返回出来：</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">yasoob</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">greet</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">now you are in the greet() function</span><span class="hl-quotes">&quot;</span><span class="hl-code">
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">welcome</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">now you are in the welcome() function</span><span class="hl-quotes">&quot;</span><span class="hl-code">
 
    </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-identifier">name</span><span class="hl-code"> == </span><span class="hl-quotes">&quot;</span><span class="hl-string">yasoob</span><span class="hl-quotes">&quot;</span><span class="hl-code">:
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">greet</span><span class="hl-code">
    </span><span class="hl-reserved">else</span><span class="hl-code">:
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">welcome</span><span class="hl-code">
 
</span><span class="hl-identifier">a</span><span class="hl-code"> = </span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">a</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: &lt;function greet at 0x7f2143c01500&gt;</span><span class="hl-code">
 
</span><span class="hl-comment">#上面清晰地展示了`a`现在指向到hi()函数中的greet()函数</span><span class="hl-code">
</span><span class="hl-comment">#现在试试这个</span><span class="hl-code">
 
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">a</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: now you are in the greet() function</span></div>
</div>
</div>
<p>再次看看这个代码。在 if/else 语句中我们返回 greet 和 welcome，而不是 greet() 和 welcome()。为什么那样？这是因为当你把一对小括号放在后面，这个函数就会执行；然而如果你不放括号在它后面，那它可以被到处传递，并且可以赋值给别的变量而不去执行它。
你明白了吗？让我再稍微多解释点细节。</p>
<p>当我们写下 <span class="marked">a = hi()</span>，hi() 会被执行，而由于 name 参数默认是 yasoob，所以函数 greet 被返回了。如果我们把语句改为 <span class="marked">a = hi(name = "ali")</span>，那么 welcome 函数将被返回。我们还可以打印出 <span class="marked">hi()()</span>，这会输出 <strong>now you are in the greet() function</strong>。</p>
<h3>将函数作为参数传给另一个函数</h3>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">hi</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">hi yasoob!</span><span class="hl-quotes">&quot;</span><span class="hl-code">
 
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">doSomethingBeforeHi</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am doing some boring work before executing hi()</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
 
</span><span class="hl-identifier">doSomethingBeforeHi</span><span class="hl-brackets">(</span><span class="hl-identifier">hi</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs:I am doing some boring work before executing hi()</span><span class="hl-code">
</span><span class="hl-comment">#        hi yasoob!</span></div>
</div>
</div>
<p>现在你已经具备所有必需知识，来进一步学习装饰器真正是什么了。装饰器让你在一个函数的前后去执行代码。</p>
<h3>
你的第一个装饰器</h3>
<p>在上一个例子里，其实我们已经创建了一个装饰器！现在我们修改下上一个装饰器，并编写一个稍微更有用点的程序：</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">a_new_decorator</span><span class="hl-brackets">(</span><span class="hl-identifier">a_func</span><span class="hl-brackets">)</span><span class="hl-code">:
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">wrapTheFunction</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am doing some boring work before executing a_func()</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
        </span><span class="hl-identifier">a_func</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
 
        </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am doing some boring work after executing a_func()</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">wrapTheFunction</span><span class="hl-code">
 
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am the function which needs some decoration to remove my foul smell</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: &quot;I am the function which needs some decoration to remove my foul smell&quot;</span><span class="hl-code">
 
</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-code"> = </span><span class="hl-identifier">a_new_decorator</span><span class="hl-brackets">(</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#now a_function_requiring_decoration is wrapped by wrapTheFunction()</span><span class="hl-code">
 
</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs:I am doing some boring work before executing a_func()</span><span class="hl-code">
</span><span class="hl-comment">#        I am the function which needs some decoration to remove my foul smell</span><span class="hl-code">
</span><span class="hl-comment">#        I am doing some boring work after executing a_func()</span></div>
</div>
</div>
<p>你看明白了吗？我们刚刚应用了之前学习到的原理。这正是 python 中装饰器做的事情！它们封装一个函数，并且用这样或者那样的方式来修改它的行为。现在你也许疑惑，我们在代码里并没有使用 <span class="marked">@</span> 符号？那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 <span class="marked">@</span> 来运行之前的代码：</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-code">@</span><span class="hl-identifier">a_new_decorator</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Hey you! Decorate me!</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-code">
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am the function which needs some decoration to </span><span class="hl-quotes">&quot;</span><span class="hl-code">
          </span><span class="hl-quotes">&quot;</span><span class="hl-string">remove my foul smell</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment">#outputs: I am doing some boring work before executing a_func()</span><span class="hl-code">
</span><span class="hl-comment">#         I am the function which needs some decoration to remove my foul smell</span><span class="hl-code">
</span><span class="hl-comment">#         I am doing some boring work after executing a_func()</span><span class="hl-code">
 
</span><span class="hl-comment">#the @a_new_decorator is just a short way of saying:</span><span class="hl-code">
</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-code"> = </span><span class="hl-identifier">a_new_decorator</span><span class="hl-brackets">(</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">)</span></div>
</div>
</div>
<p>希望你现在对 Python 装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题：</p>
<pre>print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction</pre>
<p>这并不是我们想要的！Ouput输出应该是"a_function_requiring_decoration"。这里的函数被warpTheFunction替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题，那就是functools.wraps。我们修改上一个例子来使用functools.wraps：</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">from</span><span class="hl-code"> </span><span class="hl-identifier">functools</span><span class="hl-code"> </span><span class="hl-reserved">import</span><span class="hl-code"> </span><span class="hl-identifier">wraps</span><span class="hl-code">
 
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">a_new_decorator</span><span class="hl-brackets">(</span><span class="hl-identifier">a_func</span><span class="hl-brackets">)</span><span class="hl-code">:
    @</span><span class="hl-identifier">wraps</span><span class="hl-brackets">(</span><span class="hl-identifier">a_func</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">wrapTheFunction</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am doing some boring work before executing a_func()</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-identifier">a_func</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am doing some boring work after executing a_func()</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">wrapTheFunction</span><span class="hl-code">
 
@</span><span class="hl-identifier">a_new_decorator</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Hey yo! Decorate me!</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-code">
    </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">I am the function which needs some decoration to </span><span class="hl-quotes">&quot;</span><span class="hl-code">
          </span><span class="hl-quotes">&quot;</span><span class="hl-string">remove my foul smell</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">a_function_requiring_decoration</span><span class="hl-code">.</span><span class="hl-identifier">__name__</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># Output: a_function_requiring_decoration</span></div>
</div>
</div>
<p>现在好多了。我们接下来学习装饰器的一些常用场景。</p>
<p>蓝本规范:</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">from</span><span class="hl-code"> </span><span class="hl-identifier">functools</span><span class="hl-code"> </span><span class="hl-reserved">import</span><span class="hl-code"> </span><span class="hl-identifier">wraps</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">decorator_name</span><span class="hl-brackets">(</span><span class="hl-identifier">f</span><span class="hl-brackets">)</span><span class="hl-code">:
    @</span><span class="hl-identifier">wraps</span><span class="hl-brackets">(</span><span class="hl-identifier">f</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">decorated</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-identifier">can_run</span><span class="hl-code">:
            </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-quotes">&quot;</span><span class="hl-string">Function will not run</span><span class="hl-quotes">&quot;</span><span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">f</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">decorated</span><span class="hl-code">
 
@</span><span class="hl-identifier">decorator_name</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-identifier">return</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Function is running</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
 
</span><span class="hl-identifier">can_run</span><span class="hl-code"> = </span><span class="hl-reserved">True</span><span class="hl-code">
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># Output: Function is running</span><span class="hl-code">
 
</span><span class="hl-identifier">can_run</span><span class="hl-code"> = </span><span class="hl-reserved">False</span><span class="hl-code">
</span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># Output: Function will not run</span></div>
</div>
</div>
<p><strong>注意：</strong><span class="marked">@wraps</span>接受一个函数来进行装饰，并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。</p><hr>
<h2>
使用场景</h2>
<p>现在我们来看一下装饰器在哪些地方特别耀眼，以及使用它可以让一些事情管理起来变得更简单。</p>
<h3>
授权(Authorization)</h3>
<p>装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权：</p><div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">from</span><span class="hl-code"> </span><span class="hl-identifier">functools</span><span class="hl-code"> </span><span class="hl-reserved">import</span><span class="hl-code"> </span><span class="hl-identifier">wraps</span><span class="hl-code">
 
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">requires_auth</span><span class="hl-brackets">(</span><span class="hl-identifier">f</span><span class="hl-brackets">)</span><span class="hl-code">:
    @</span><span class="hl-identifier">wraps</span><span class="hl-brackets">(</span><span class="hl-identifier">f</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">decorated</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-identifier">auth</span><span class="hl-code"> = </span><span class="hl-identifier">request</span><span class="hl-code">.</span><span class="hl-identifier">authorization</span><span class="hl-code">
        </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-identifier">auth</span><span class="hl-code"> </span><span class="hl-reserved">or</span><span class="hl-code"> </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-identifier">check_auth</span><span class="hl-brackets">(</span><span class="hl-identifier">auth</span><span class="hl-code">.</span><span class="hl-identifier">username</span><span class="hl-code">, </span><span class="hl-identifier">auth</span><span class="hl-code">.</span><span class="hl-identifier">password</span><span class="hl-brackets">)</span><span class="hl-code">:
            </span><span class="hl-identifier">authenticate</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">f</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">decorated</span></div>
</div>
</div>
<h3>
日志(Logging)</h3>
<p>日志是装饰器运用的另一个亮点。这是个例子：</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">from</span><span class="hl-code"> </span><span class="hl-identifier">functools</span><span class="hl-code"> </span><span class="hl-reserved">import</span><span class="hl-code"> </span><span class="hl-identifier">wraps</span><span class="hl-code">
 
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">logit</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">:
    @</span><span class="hl-identifier">wraps</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">with_logging</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-code">.</span><span class="hl-identifier">__name__</span><span class="hl-code"> + </span><span class="hl-quotes">&quot;</span><span class="hl-string"> was called</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">with_logging</span><span class="hl-code">
 
@</span><span class="hl-identifier">logit</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">addition_func</span><span class="hl-brackets">(</span><span class="hl-identifier">x</span><span class="hl-brackets">)</span><span class="hl-code">:
   </span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Do some math.</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-code">
   </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">x</span><span class="hl-code"> + </span><span class="hl-identifier">x</span><span class="hl-code">
 
 
</span><span class="hl-identifier">result</span><span class="hl-code"> = </span><span class="hl-identifier">addition_func</span><span class="hl-brackets">(</span><span class="hl-number">4</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># Output: addition_func was called</span></div>
</div>
</div>
<p>我敢肯定你已经在思考装饰器的一个其他聪明用法了。</p>
<hr><h2>
带参数的装饰器</h2>
<p>来想想这个问题，难道@wraps不也是个装饰器吗？但是，它接收一个参数，就像任何普通的函数能做的那样。那么，为什么我们不也那样做呢？
这是因为，当你使用@my_decorator语法时，你是在应用一个以单个函数作为参数的一个包裹函数。记住，Python里每个东西都是一个对象，而且这包括函数！记住了这些，我们可以编写一下能返回一个包裹函数的函数。</p>
<h3>在函数中嵌入装饰器</h3>
<p>我们回到日志的例子，并创建一个包裹函数，能让我们指定一个用于输出的日志文件。</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">from</span><span class="hl-code"> </span><span class="hl-identifier">functools</span><span class="hl-code"> </span><span class="hl-reserved">import</span><span class="hl-code"> </span><span class="hl-identifier">wraps</span><span class="hl-code">
 
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">logit</span><span class="hl-brackets">(</span><span class="hl-identifier">logfile</span><span class="hl-code">=</span><span class="hl-quotes">'</span><span class="hl-string">out.log</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">logging_decorator</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">:
        @</span><span class="hl-identifier">wraps</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">wrapped_function</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">:
            </span><span class="hl-identifier">log_string</span><span class="hl-code"> = </span><span class="hl-identifier">func</span><span class="hl-code">.</span><span class="hl-identifier">__name__</span><span class="hl-code"> + </span><span class="hl-quotes">&quot;</span><span class="hl-string"> was called</span><span class="hl-quotes">&quot;</span><span class="hl-code">
            </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">log_string</span><span class="hl-brackets">)</span><span class="hl-code">
            </span><span class="hl-comment"># 打开logfile，并写入内容</span><span class="hl-code">
            </span><span class="hl-identifier">with</span><span class="hl-code"> </span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">logfile</span><span class="hl-code">, </span><span class="hl-quotes">'</span><span class="hl-string">a</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-identifier">as</span><span class="hl-code"> </span><span class="hl-identifier">opened_file</span><span class="hl-code">:
                </span><span class="hl-comment"># 现在将日志打到指定的logfile</span><span class="hl-code">
                </span><span class="hl-identifier">opened_file</span><span class="hl-code">.</span><span class="hl-identifier">write</span><span class="hl-brackets">(</span><span class="hl-identifier">log_string</span><span class="hl-code"> + </span><span class="hl-quotes">'</span><span class="hl-special">\n</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">
            </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">wrapped_function</span><span class="hl-code">
    </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">logging_decorator</span><span class="hl-code">
 
@</span><span class="hl-identifier">logit</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">myfunc1</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">pass</span><span class="hl-code">
 
</span><span class="hl-identifier">myfunc1</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># Output: myfunc1 was called</span><span class="hl-code">
</span><span class="hl-comment"># 现在一个叫做 out.log 的文件出现了，里面的内容就是上面的字符串</span><span class="hl-code">
 
@</span><span class="hl-identifier">logit</span><span class="hl-brackets">(</span><span class="hl-identifier">logfile</span><span class="hl-code">=</span><span class="hl-quotes">'</span><span class="hl-string">func2.log</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">myfunc2</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">pass</span><span class="hl-code">
 
</span><span class="hl-identifier">myfunc2</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
</span><span class="hl-comment"># Output: myfunc2 was called</span><span class="hl-code">
</span><span class="hl-comment"># 现在一个叫做 func2.log 的文件出现了，里面的内容就是上面的字符串</span></div>
</div>
</div>
<hr><h2>
装饰器类</h2>
<p>现在我们有了能用于正式环境的logit装饰器，但当我们的应用的某些部分还比较脆弱时，异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email，同时也保留日志，留个记录。这是一个使用继承的场景，但目前为止我们只看到过用来构建装饰器的函数。</p>
<p>幸运的是，类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式，来重新构建logit。</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">from</span><span class="hl-code"> </span><span class="hl-identifier">functools</span><span class="hl-code"> </span><span class="hl-reserved">import</span><span class="hl-code"> </span><span class="hl-identifier">wraps</span><span class="hl-code">
 
</span><span class="hl-reserved">class</span><span class="hl-code"> </span><span class="hl-identifier">logit</span><span class="hl-brackets">(</span><span class="hl-identifier">object</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">logfile</span><span class="hl-code">=</span><span class="hl-quotes">'</span><span class="hl-string">out.log</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">logfile</span><span class="hl-code"> = </span><span class="hl-identifier">logfile</span><span class="hl-code">
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">__call__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">:
        @</span><span class="hl-identifier">wraps</span><span class="hl-brackets">(</span><span class="hl-identifier">func</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">wrapped_function</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">:
            </span><span class="hl-identifier">log_string</span><span class="hl-code"> = </span><span class="hl-identifier">func</span><span class="hl-code">.</span><span class="hl-identifier">__name__</span><span class="hl-code"> + </span><span class="hl-quotes">&quot;</span><span class="hl-string"> was called</span><span class="hl-quotes">&quot;</span><span class="hl-code">
            </span><span class="hl-identifier">print</span><span class="hl-brackets">(</span><span class="hl-identifier">log_string</span><span class="hl-brackets">)</span><span class="hl-code">
            </span><span class="hl-comment"># 打开logfile并写入</span><span class="hl-code">
            </span><span class="hl-identifier">with</span><span class="hl-code"> </span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">logfile</span><span class="hl-code">, </span><span class="hl-quotes">'</span><span class="hl-string">a</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-identifier">as</span><span class="hl-code"> </span><span class="hl-identifier">opened_file</span><span class="hl-code">:
                </span><span class="hl-comment"># 现在将日志打到指定的文件</span><span class="hl-code">
                </span><span class="hl-identifier">opened_file</span><span class="hl-code">.</span><span class="hl-identifier">write</span><span class="hl-brackets">(</span><span class="hl-identifier">log_string</span><span class="hl-code"> + </span><span class="hl-quotes">'</span><span class="hl-special">\n</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">
            </span><span class="hl-comment"># 现在，发送一个通知</span><span class="hl-code">
            </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">notify</span><span class="hl-brackets">(</span><span class="hl-brackets">)</span><span class="hl-code">
            </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">func</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">wrapped_function</span><span class="hl-code">
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">notify</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-comment"># logit只打日志，不做别的</span><span class="hl-code">
        </span><span class="hl-reserved">pass</span></div>
</div>
</div>
<p>这个实现有一个附加优势，在于比嵌套函数的方式更加整洁，而且包裹一个函数还是使用跟以前一样的语法：</p>
<pre>@logit()
def myfunc1():
    pass</pre>
<p>现在，我们给 logit 创建子类，来添加 email 的功能(虽然 email 这个话题不会在这里展开)。</p>
<div class="example"> 
<div class="example_code">
<div class="hl-main"><span class="hl-reserved">class</span><span class="hl-code"> </span><span class="hl-identifier">email_logit</span><span class="hl-brackets">(</span><span class="hl-identifier">logit</span><span class="hl-brackets">)</span><span class="hl-code">:
    </span><span class="hl-quotes">'''</span><span class="hl-string">
    一个logit的实现版本，可以在函数调用时发送email给管理员
    </span><span class="hl-quotes">'''</span><span class="hl-code">
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">email</span><span class="hl-code">=</span><span class="hl-quotes">'</span><span class="hl-string">admin@myproject.com</span><span class="hl-quotes">'</span><span class="hl-code">, *</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">email</span><span class="hl-code"> = </span><span class="hl-identifier">email</span><span class="hl-code">
        </span><span class="hl-builtin">super</span><span class="hl-brackets">(</span><span class="hl-identifier">email_logit</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-code">.</span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">args</span><span class="hl-code">, **</span><span class="hl-identifier">kwargs</span><span class="hl-brackets">)</span><span class="hl-code">
 
    </span><span class="hl-reserved">def</span><span class="hl-code"> </span><span class="hl-identifier">notify</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-code">:
        </span><span class="hl-comment"># 发送一封email到self.email</span><span class="hl-code">
        </span><span class="hl-comment"># 这里就不做实现了</span><span class="hl-code">
        </span><span class="hl-reserved">pass</span></div>
</div>
</div>
<p>从现在起，@email_logit 将会和 @logit 产生同样的效果，但是在打日志的基础上，还会多发送一封邮件给管理员。</p>


<blockquote><p>原文地址：https://eastlakeside.gitbooks.io/interpy-zh/content/decorators/</p></blockquote>

				</div>
			</div>
			<div class="previous-next-links">
			<div class="previous-design-link">← <a href="../w3cnote/c-volatile-keyword.html" rel="prev"> C/C++ 中 volatile 关键字详解</a> </div>
			<div class="next-design-link"><a href="../w3cnote/strlen-and-sizeof.html" rel="next"> 利用 strlen 和 sizeof 求取字符串长度注意事项</a> →</div>
			</div>
			<style>
.wrapper {
  /*text-transform: uppercase; */
  background: #ececec;
  color: #555;
  cursor: help;
  font-family: "Gill Sans", Impact, sans-serif;
  font-size: 20px;
  position: relative;
  text-align: center;
  width: 200px;
  -webkit-transform: translateZ(0); /* webkit flicker fix */
  -webkit-font-smoothing: antialiased; /* webkit text rendering fix */
}

.wrapper .tooltip {
  white-space: nowrap;
  font-size: 14px;
  text-align: left;
  background: #96b97d;
  bottom: 100%;
  color: #fff;
  display: block;
  left: -25px;
  margin-bottom: 15px;
  opacity: 0;
  padding: 14px;
  pointer-events: none;
  position: absolute;
  
  -webkit-transform: translateY(10px);
     -moz-transform: translateY(10px);
      -ms-transform: translateY(10px);
       -o-transform: translateY(10px);
          transform: translateY(10px);
  -webkit-transition: all .25s ease-out;
     -moz-transition: all .25s ease-out;
      -ms-transition: all .25s ease-out;
       -o-transition: all .25s ease-out;
          transition: all .25s ease-out;
  -webkit-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
     -moz-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
      -ms-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
       -o-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
          box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
}
.tooltip a {
	color:#fff;
}
/* This bridges the gap so you can mouse into the tooltip without it disappearing */
.wrapper .tooltip:before {
  bottom: -20px;
  content: " ";
  display: block;
  height: 20px;
  left: 0;
  position: absolute;
  width: 100%;
}  

/* CSS Triangles - see Trevor's post */
.wrapper .tooltip:after {
  border-left: solid transparent 10px;
  border-right: solid transparent 10px;
  border-top: solid #96b97d 10px;
  bottom: -10px;
  content: " ";
  height: 0;
  left: 20%;
  margin-left: -13px;
  position: absolute;
  width: 0;
}
.wrapper .tooltip1 {
	margin-left: 50px;
	padding-top: 0px;
}
/*
.wrapper:hover .tooltip {
  opacity: 1;
  pointer-events: auto;
  -webkit-transform: translateY(0px);
     -moz-transform: translateY(0px);
      -ms-transform: translateY(0px);
       -o-transform: translateY(0px);
          transform: translateY(0px);
}
*/
/* IE can just show/hide with no transition */
.lte8 .wrapper .tooltip {
  display: none;
}

.lte8 .wrapper:hover .tooltip {
  display: block;
}

</style>

<link rel="stylesheet" href="../0/upvotejs.css">
<script src=""></script>
<script src=""></script>
<div class="title" id="comments">
	<h2 class="">
    <div class="altblock">
				<i style="font-size:28px;margin-top: 8px;" class="fa fa-plus-square" aria-hidden="true"></i>
		    </div>
    <span class="mw-headline" id="qa_headline">4  篇笔记</span>
	<span class="mw-headline" id="user_add_note" style="float:right;line-height: 62px;padding-right: 14px;"><i class="fa fa-pencil-square-o" aria-hidden="true"></i>  写笔记</span>
    </h2>
</div>

<div id="postcomments"  style="display:none;" >
	<ol class="commentlist">
		<li class="comment byuser comment-author-tianqixin bypostauthor even thread-even depth-1" id="comment-30620"><span class="comt-f">#0</span><div class="comt-avatar wrapper"><i style="font-size:36px;" class="fa fa-user-circle" aria-hidden="true"></i><div class="tooltip"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;fqas</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;234***657@qq.com</p><p><i class="fa fa-external-link" aria-hidden="true"></i> <a rel="nofollow" target="_blank" href="https://foofish.net/python-decorator.html">&nbsp;&nbsp;参考地址</a></p></div><div id="runoobvote-id-30620" data-commid = "30620" class="upvotejs"><a class="upvote"></a> <span class="count">800</span></div></div><div class="comt-main" id="div-comment-30620"><p>讲 Python 装饰器前，我想先举个例子，虽有点污，但跟装饰器这个话题很贴切。</p>

<p>每个人都有的内裤主要功能是用来遮羞，但是到了冬天它没法为我们防风御寒，咋办？我们想到的一个办法就是把内裤改造一下，让它变得更厚更长，这样一来，它不仅有遮羞功能，还能提供保暖，不过有个问题，这个内裤被我们改造成了长裤后，虽然还有遮羞功能，但本质上它不再是一条真正的内裤了。于是聪明的人们发明长裤，在不影响内裤的前提下，直接把长裤套在了内裤外面，这样内裤还是内裤，有了长裤后宝宝再也不冷了。装饰器就像我们这里说的长裤，在不影响内裤作用的前提下，给我们的身子提供了保暖的功效。</p>

<p>谈装饰器前，还要先要明白一件事，Python 中的函数和 Java、C++不太一样，Python 中的函数可以像普通变量一样当做参数传递给另外一个函数，例如：</p>

<pre>def foo():
    print("foo")

def bar(func):
    func()

bar(foo)</pre>
<p>正式回到我们的主题。装饰器本质上是一个 Python 函数或类，它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能，装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景，比如：插入日志、性能测试、事务处理、缓存、权限校验等场景，装饰器是解决这类问题的绝佳设计。有了装饰器，我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲，装饰器的作用就是为已经存在的对象添加额外的功能。</p>

<p>先来看一个简单例子，虽然实际代码可能比这复杂很多：</p>

<pre>def foo():
    print('i am foo')</pre>
<p>现在有一个新的需求，希望可以记录下函数的执行日志，于是在代码中添加日志代码：</p>

<pre>def foo():
    print('i am foo')
    logging.info("foo is running")</pre>
<p>如果函数 bar()、bar2() 也有类似的需求，怎么做？再写一个 logging 在 bar 函数里？这样就造成大量雷同的代码，为了减少重复写代码，我们可以这样做，重新定义一个新的函数：专门处理日志 ，日志处理完之后再执行真正的业务代码</p>

<pre>def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    func()

def foo():
    print('i am foo')

use_logging(foo)</pre>
<p>这样做逻辑上是没问题的，功能是实现了，但是我们调用的时候不再是调用真正的业务逻辑 foo 函数，而是换成了 use_logging 函数，这就破坏了原有的代码结构， 现在我们不得不每次都要把原来的那个 foo 函数作为参数传递给 use_logging 函数，那么有没有更好的方式的呢？当然有，答案就是装饰器。</p>

<p><strong>简单装饰器</strong></p>
<pre>def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()   # 把 foo 当做参数传递进来时，执行func()就相当于执行foo()
    return wrapper

def foo():
    print('i am foo')

foo = use_logging(foo)  # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper，这条语句相当于  foo = wrapper
foo()                   # 执行foo()就相当于执行 wrapper()</pre>
<p>use_logging 就是一个装饰器，它一个普通的函数，它把执行真正业务逻辑的函数 func 包裹在其中，看起来像 foo 被 use_logging 装饰了一样，use_logging 返回的也是一个函数，这个函数的名字叫 wrapper。在这个例子中，函数进入和退出时 ，被称为一个横切面，这种编程方式被称为面向切面的编程。</p>

<p><strong>@ 语法糖</strong></p>
<p>如果你接触 Python 有一段时间了的话，想必你对 @ 符号一定不陌生了，没错 @ 符号就是装饰器的语法糖，它放在函数开始定义的地方，这样就可以省略最后一步再次赋值的操作。</p>

<pre>def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()
    return wrapper

@use_logging
def foo():
    print("i am foo")

foo()</pre>
<p>如上所示，有了 @ ，我们就可以省去foo = use_logging(foo)这一句了，直接调用 foo() 即可得到想要的结果。你们看到了没有，foo() 函数不需要做任何修改，只需在定义的地方加上装饰器，调用的时候还是和以前一样，如果我们有其他的类似函数，我们可以继续调用装饰器来修饰函数，而不用重复修改函数或者增加新的封装。这样，我们就提高了程序的可重复利用性，并增加了程序的可读性。</p>

<p>装饰器在 Python 使用如此方便都要归因于 Python 的函数能像普通的对象一样能作为参数传递给其他函数，可以被赋值给其他变量，可以作为返回值，可以被定义在另外一个函数内。</p>

<p><strong>*args、**kwargs</strong></p>
<p>可能有人问，如果我的业务逻辑函数 foo 需要参数怎么办？比如：</p>

<pre>def foo(name):
    print("i am %s" % name)</pre>
<p>我们可以在定义 wrapper 函数的时候指定参数：</p>

<pre>def wrapper(name):
        logging.warn("%s is running" % func.__name__)
        return func(name)
    return wrapper</pre>
<p>这样 foo 函数定义的参数就可以定义在 wrapper 函数中。这时，又有人要问了，如果 foo 函数接收两个参数呢？三个参数呢？更有甚者，我可能传很多个。当装饰器不知道 foo 到底有多少个参数时，我们可以用 *args 来代替：</p>

<pre>def wrapper(*args):
        logging.warn("%s is running" % func.__name__)
        return func(*args)
    return wrapper</pre>
<p>如此一来，甭管 foo 定义了多少个参数，我都可以完整地传递到 func 中去。这样就不影响 foo 的业务逻辑了。这时还有读者会问，如果 foo 函数还定义了一些关键字参数呢？比如：</p>

<pre>def foo(name, age=None, height=None):
    print("I am %s, age %s, height %s" % (name, age, height))</pre>
<p>这时，你就可以把 wrapper 函数指定关键字函数：</p>

<pre>def wrapper(*args, **kwargs):
        # args是一个数组，kwargs一个字典
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper</pre>
<p><strong>带参数的装饰器</strong></p>
<p>装饰器还有更大的灵活性，例如带参数的装饰器，在上面的装饰器调用中，该装饰器接收唯一的参数就是执行业务的函数 foo 。装饰器的语法允许我们在调用时，提供其它参数，比如@decorator(a)。这样，就为装饰器的编写和使用提供了更大的灵活性。比如，我们可以在装饰器中指定日志的等级，因为不同业务函数可能需要的日志级别是不一样的。</p>

<pre>def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()</pre>
<p>上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装，并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")调用的时候，Python 能够发现这一层的封装，并把参数传递到装饰器的环境中。</p>

<p><span class="marked">@use_logging(level="warn")</span> 等价于 <span class="marked">@decorator</span></p>

<p><strong>类装饰器</strong></p>
<p>没错，装饰器不仅可以是函数，还可以是类，相比函数装饰器，类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法，当使用 @ 形式将装饰器附加到函数上时，就会调用此方法。</p>

<pre>class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()
functools.wraps</pre>
<p>使用装饰器极大地复用了代码，但是他有一个缺点就是原函数的元信息不见了，比如函数的docstring、__name__、参数列表，先看例子：</p>

<pre># 装饰器
def logged(func):
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'with_logging'
        print func.__doc__       # 输出 None
        return func(*args, **kwargs)
    return with_logging

# 函数
@logged
def f(x):
   """does some math"""
   return x + x * x

logged(f)</pre>
<p>不难发现，函数 f 被with_logging取代了，当然它的docstring，__name__就是变成了with_logging函数的信息了。好在我们有functools.wraps，wraps本身也是一个装饰器，它能把原函数的元信息拷贝到装饰器里面的 func 函数中，这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。</p>

<pre>from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'f'
        print func.__doc__       # 输出 'does some math'
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x</pre>
<p><strong>装饰器顺序</strong></p>
<p>一个函数还可以同时定义多个装饰器，比如：</p>

<pre>@a
@b
@c
def f ():
    pass</pre>
<p>它的执行顺序是从里到外，最先调用最里层的装饰器，最后调用最外层的装饰器，它等效于</p>

<pre>f = a(b(c(f)))</pre><div class="comt-meta wrapper"><span class="comt-author"><a target="_blank" href="javascript:;">fqas</a><div class="tooltip tooltip1"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;fqas</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;234***657@qq.com</p><p><i class="fa fa-external-link" aria-hidden="true"></i> <a rel="nofollow" target="_blank" href="https://foofish.net/python-decorator.html">&nbsp;&nbsp;参考地址</a></p></div></span>5年前 (2018-07-29)</div></div></li><!-- #comment-## -->
<li class="comment odd alt thread-odd thread-alt depth-1" id="comment-53580"><span class="comt-f">#0</span><div class="comt-avatar wrapper"><i style="font-size:36px;" class="fa fa-user-circle" aria-hidden="true"></i><div class="tooltip"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;DragonZ</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;246***7769@qq.com</p></div><div id="runoobvote-id-53580" data-commid = "53580" class="upvotejs"><a class="upvote"></a> <span class="count">47</span></div></div><div class="comt-main" id="div-comment-53580"><p>@wraps() 相当于：</p>

<pre>def Wraps(fWrap):
    def TmpWraps(func):
        def WrapsDecorator(*args, **kwargs):
            WrapsDecorator.__name__=fWrap.__name__
            WrapsDecorator.__doc__=fWrap.__doc__
            return func(*args, **kwargs)
        return WrapsDecorator
    return TmpWraps</pre>
<p>示例代码如下：</p>

<pre>def Wraps(fWrap):
    def TmpWraps(func):
        def WrapsDecorator(*args, **kwargs):
            WrapsDecorator.__name__=fWrap.__name__
            WrapsDecorator.__doc__=fWrap.__doc__
            return func(*args, **kwargs)
        return WrapsDecorator
    return TmpWraps


def NewDecorator(level):
    def TmpDecorator(func):
        def anotherFunc():
            '''it's another function'''
            pass
        #from functools import wraps
        #@wraps(anotherFunc)
        @Wraps(anotherFunc)
        def NewFibonacci(*args, **kwargs):
            print('[%s]func: %s is called, %s'%(level,func.__name__,NewFibonacci.__doc__))
            from time import time
            start = time()
            rst=func(*args, **kwargs)
            end=time()
            print('cost time: %.2fs'%(end-start))
            return rst
        return NewFibonacci
    return TmpDecorator


@NewDecorator('log')
def Fibonacci_print(num=10):
    if num &lt;= 0:
        return
    minNum,maxNum=0,1
    while num &gt; 0:
        tmpNum=minNum
        print(tmpNum, end=',')
        minNum=maxNum
        maxNum+=tmpNum
        num-=1

Fibonacci_print()
print(Fibonacci_print.__name__)</pre>
<p>输出为：</p>

<pre>[log]func: Fibonacci_print is called, it's another function
0,1,1,2,3,5,8,13,21,34,cost time: 0.07s
anotherFunc</pre><div class="comt-meta wrapper"><span class="comt-author"><a target="_blank" href="javascript:;">DragonZ</a><div class="tooltip tooltip1"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;DragonZ</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;246***7769@qq.com</p></div></span>3年前 (2020-05-13)</div></div></li><!-- #comment-## -->
<li class="comment even thread-even depth-1" id="comment-61459"><span class="comt-f">#0</span><div class="comt-avatar wrapper"><i style="font-size:36px;" class="fa fa-user-circle" aria-hidden="true"></i><div class="tooltip"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;闫伟超</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;yif***chaoran@163.com</p></div><div id="runoobvote-id-61459" data-commid = "61459" class="upvotejs"><a class="upvote"></a> <span class="count">27</span></div></div><div class="comt-main" id="div-comment-61459"><p>再论 <span class="marked">@wraps(func)</span> 装饰器实现原理。</p>
<p>
纠正下楼上 <span class="marked">@DragonZ</span> 描述的基本代码框架，应该是如下：</p>

<pre>def Wraps(fWrap):
    def TmpWraps(func):
        def WrapsDecorator(*args, **kwargs):
            return func(*args, **kwargs)
            WrapsDecorator.__name__=fWrap.__name__
            WrapsDecorator.__doc__=fWrap.__doc__
        return WrapsDecorator
    return TmpWraps</pre>
<p>并且该装饰器不仅仅只是复制了__name__ 和 __doc__ 函数元信息，还有更多其他信息，正确的实现代码如下：</p>

<pre>def mywraps(fwrap):
    def TmpWraps(func):
        def dec(**args):
            return func(**args)
        meta_info=['__module__', '__name__', '__qualname__', '__doc__', '__annotations__']
        for meta in meta_info:
            setattr(dec,meta,getattr(fwrap,meta))
        #逐个获取fwrap函数的元信息，并复制到dec函数上
        return dec
    return TmpWraps</pre><p>
另外，针对类装饰器，可能下面这样解释，会对初学者更加友好：</p>
<p>
装饰器还可以通过类来实现，其实主要是利用类的以下特点来变相实现函数装饰器功能：</p>
<p>
函数调用语语法f()等同于类的实例化，即调用类的__init__函数创建对象</p><p>
对象的调用obj()等同于运行对象的__call__魔法函数</p><p>
通过类实现装饰器，可以避免函数装饰器超过2层的嵌套情况，因为如果有三层的话，最外层函数可以认为是在调用类的__init__函数，这样可以让代码更易读和维护</p><p>
本质，只要实现类的__init__和__call__魔法函数，并在__init__函数内接受装饰器参数，在__call__函数内实现具体装饰器结构即可</p><p>
下面举例，用类实现带参装饰器，可以观察下不同</p>

<pre>from functools import wraps 
#定义一个装饰器名称的类
class  with_para_decorator: 
    #在类的__init__函数内接受装饰器参数，并赋值给类的实例参数，这样可以让其他函数随时使用
    #当然，如果装饰器没有参数，此处不转a,b即可，相当于类无参实例化
    def __init__(self,a,b):    
        self.a=a
        self.b=b
    #在类的__call__函数内接受被装饰函数，并具体定义装饰器
    def __call__(self,func):   
    @wraps(func)   
    def wrap_function(arg1,arg2):  
        print('装饰带参数的函数，函数传的参数为：{0}, {1}'.format(arg1,arg2))
        print('带参数的装饰器，装饰器传的参数为：{0}, {1}'.format(self.a,self.b))
        return func(arg1,arg2)   
    return wrap_function
#使用装饰器
@with_para_decorator(1,2)  
def need_decorate(a,b):   
pass
need_decorate(4,5) 
</pre>
<p>以上代码具体原理解析如下：</p>
<ul><li>
@with_para_decorator(1,2)，因为是类的名称，相当于使用(1,2)参数创建并返回该类的一个实例对象，比如是 obj</li><li>
此时，语法变为@obj，相当于need_decorate=obj(need_decorate)，此时会调用obj.__call__魔法函数，而我们在该魔法函数具体实现了装饰器功能</li><li>
可以看到，其本质的运行原理，和函数装饰器没区别，只是将三层函数嵌套，变成了一个__init__函数和__call__函数的两层嵌套</li><li>
对比下来，可以看到，类装饰器，代码更加直观</li></ul><div class="comt-meta wrapper"><span class="comt-author"><a target="_blank" href="javascript:;">闫伟超</a><div class="tooltip tooltip1"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;闫伟超</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;yif***chaoran@163.com</p></div></span>2年前 (2021-02-20)</div></div></li><!-- #comment-## -->
<li class="comment odd alt thread-odd thread-alt depth-1" id="comment-73848"><span class="comt-f">#0</span><div class="comt-avatar wrapper"><i style="font-size:36px;" class="fa fa-user-circle" aria-hidden="true"></i><div class="tooltip"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;ChaseTheLight</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;cha***the_light@foxmail.com</p></div><div id="runoobvote-id-73848" data-commid = "73848" class="upvotejs"><a class="upvote"></a> <span class="count">19</span></div></div><div class="comt-main" id="div-comment-73848"><p>纠正一下目前最高赞的笔记（<a href="#div-comment-30620" rel="nofollow ugc"><span class="marked">@fqas</span></a>）中"类装饰器"关于 <strong>functools.wraps</strong> 的应用场景的阐述:</p>

<p>样例代码中不管加不加 <strong>wraps</strong> 装饰器，结果都会符合预期。</p>

<p>样例代码：</p>

<pre>def logged(func):
    def with_logging(*args, **kwargs):
        print(func.__name__)     # 输出 'with_logging'
        print(func.__doc__)      # 输出 None
        return func(*args, **kwargs)
    return with_logging

# 函数
@logged
def f(x):
   """does some math"""
   return x + x * x


logged(f)</pre><p>
首先，调用方法就不对，最后一行换成 <strong>f(1)</strong>可能才是他原本想要的结果。</p>
<p>
其次，样例中的两行注释是错的，加不加 <span class="marked">@wraps(func)</span> 都一样，符合原本的预期，只有在装饰器的外部想要调用被装饰对象的属性时，加 <strong>wraps</strong> 才有意义，比如：</p>

<pre>def logged(func):    
    def with_logging(*args, **kwargs):        
        print("装饰器")        
        return func(*args, **kwargs)    
    return with_logging
# 函数
# @logged
def f(x):   
    """does some math"""   
    return x + x * x

f(2)

print(f.__name__ )     # 输出 'with_logging'
print(f.__doc__)       # 输出 None</pre>
<p>这个时候，结果才符合样例中的两条注释，需要加上 <strong>wraps</strong> 装饰器变成：</p>

<pre>from functools import wraps
def logged(func):
    @wraps(func)    
    def with_logging(*args, **kwargs):        
        print("装饰器")        
        return func(*args, **kwargs)    
    return with_logging


# 函数
@logged
def f(x):
   """does some math"""
   return x + x * x


f(2)

print(f.__name__ )     # 输出 'f'
print(f.__doc__)       # 输出 'does some math'
</pre><div class="comt-meta wrapper"><span class="comt-author"><a target="_blank" href="javascript:;">ChaseTheLight</a><div class="tooltip tooltip1"><p><i class="fa fa-user" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp;ChaseTheLight</p><p><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;&nbsp;cha***the_light@foxmail.com</p></div></span>10个月前 (07-11)</div></div></li><!-- #comment-## -->
	</ol>
	<div class="pagenav">
			</div>
</div>
<div id="respond" class="no_webshot"> 
		<div class="comment-signarea" style="display:none; padding: 20px 20px;"> 
	<h3 class="text-muted" id="share_code" style="color: #799961;"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> 点我分享笔记</h3>
	<!--
	<p style="font-size:14px;">笔记需要是本篇文章的内容扩展！</p><br>
	<p style="font-size:12px;"><a href="../tougao" target="_blank">文章投稿，可点击这里</a></p>
	<p style="font-size:14px;"><a href="../w3cnote/runoob-user-test-intro.html#invite" target="_blank">注册邀请码获取方式</a></p>
		<h3 class="text-muted"><i class="fa fa-info-circle" aria-hidden="true"></i> 分享笔记前必须<a href="javascript:;" class="runoob-pop">登录</a>！</h3>
		<p><a href="../w3cnote/runoob-user-test-intro.html#invite" target="_blank">注册邀请码获取方式</a></p>-->
	</div>
		
	<form action="/wp-content/themes/runoob/option/addnote.php" method="post" id="commentform" style="display:none;">
		<div class="comt">
			<div class="comt-title">
				<i style="font-size:36px;" class="fa fa-user-circle" aria-hidden="true"></i>				<p><a id="cancel-comment-reply-link" href="javascript:;">取消</a></p>
			</div>
			<div class="comt-box">
			<div id="mded"></div>
			
				<div class="comt-ctrl">
					<div class="comt-tips"><input type='hidden' name='comment_post_ID' value='20470' id='comment_post_ID' />
<input type='hidden' name='comment_parent' id='comment_parent' value='0' />
</div>
					<button type="submit" name="submit" id="submit" tabindex="5"><i class="fa fa-pencil" aria-hidden="true"></i> 分享笔记</button>
				</div>
			</div>
		
				
					<div class="comt-comterinfo"> 
						<ul id="comment-author-info">
							<li class="form-inline"><label class="hide" for="author">昵称</label><input class="ipt" type="text" name="author" id="author" value="" tabindex="2" placeholder="昵称"><span class="text-muted">昵称 (必填)</span></li>
							<li class="form-inline"><label class="hide" for="email">邮箱</label><input class="ipt" type="text" name="email" id="email" value="" tabindex="3" placeholder="邮箱"><span class="text-muted">邮箱 (必填)</span></li>
							<li class="form-inline"><label class="hide" for="url">引用地址</label><input class="ipt" type="text" name="url" id="url" value="" tabindex="4" placeholder="引用地址"><span class="text-muted">引用地址</span></li>
						</ul>
					</div>
				
			
		</div>

	</form>
	</div>
<script type="text/javascript">
$(function() {
	//初始化编辑器
	
	var editor = new Simditor({
	  textarea: $('#mded'),
	  placeholder: '写笔记...',
	  upload:false,
	 // upload: {url:'/api/comment_upload_file.php',params: null,fileKey: 'upload_file',connectionCount: 1,leaveConfirm: '文件正在上传，您确定离开?'},
	  defaultImage: 'https://www.runoob.com/images/logo.png',
	  codeLanguages: '',
	  autosave: 'editor-content',
	  toolbar: [  'bold','code','ul','ol','image' ]
	});
	editor.on('selectionchanged', function() {
		$(".code-popover").hide();
	});

	// 提交数据
	$("#share_code").click(function() {
		$(".comment-signarea").hide();
		$("#commentform").show();
		
	});
	$("#user_add_note").click(function() {
		$(".comment-signarea").hide();
		$("#commentform").show();
		$('html, body').animate({
       	    scrollTop: $("#respond").offset().top
    	}, 200);
	});

	// 提交笔记
	var commentform=$('#commentform');
	commentform.prepend('<div id="comment-status" style="display:none;" ></div>');
	var statusdiv=$('#comment-status');
	
	commentform.submit(function(e){
		e.preventDefault();
		var noteContent = editor.getValue();
		// console.log(noteContent);
		noteContent = noteContent.replace(/<pre><code>/g,"<pre>");
		noteContent = noteContent.replace(/<\/code><\/pre>/g,"</pre>");
		
		// 系列化表单数据
		var comment_parent = 0;
		var is_user_logged_in = $("#is_user_logged_in").val();
		var comment_post_ID =  20470;
		var _wp_unfiltered_html_comment = $("#_wp_unfiltered_html_comment").val();
		var comment = noteContent;
		var author = $("#author").val();
		var url = $("#url").val();
		var email = $("#email").val();
		if(isBlank(author) && is_user_logged_in==0) {
			statusdiv.html('<p  class="ajax-error">请输入昵称！</p>').show();
		} else if(isBlank(email)  && is_user_logged_in==0) {
			statusdiv.html('<p  class="ajax-error">请输入邮箱！</p>').show();
		} else {
			// var formdata=commentform.serialize() + "&comment=" + noteContent ;
			// 添加状态信息
			statusdiv.html('<p>Processing...</p>').show();
			// 获取表单提交地址
			var formurl=commentform.attr('action');
			
			// 异步提交
			$.ajax({
					type: 'post',
					url: formurl,
					dataType:'json',
					data: {"comment_parent":comment_parent,"comment_post_ID":comment_post_ID, "_wp_unfiltered_html_comment":_wp_unfiltered_html_comment,"comment":comment,"url":url, "email":email,"author":author},
					error: function(XMLHttpRequest, textStatus, errorThrown){
					statusdiv.html('<p class="ajax-error" >数据不完整或表单提交太快了！</p>').show();
				},
				success: function(data, textStatus){
					if(data.errorno=="0") {
						$("#submit").prop('disabled', true);
						statusdiv.html('<p class="ajax-success" >笔记已提交审核，感谢分享笔记！</p>').show();
						alert('笔记已提交审核，感谢分享笔记！');
					}else{
						statusdiv.html('<p class="ajax-error" >'+data.msg+'</p>').show();
					}
					commentform.find('textarea[name=comment]').val('');
				}
			});
			setTimeout(function(){
		        $("#submit").prop('disabled', false);
		    }, 10*1000);
		}
		return false;

	});
	$(".comt-author").click(function() {
		href = $(this).children("a").attr("href");
		if(href.indexOf("/note/")!=-1) {
			var win = window.open(href, '_blank');
  			win.focus();
		}
	});
	$(".comt-meta span").hover(function(){
		$(this).children(".tooltip").css({ "opacity": 1, "pointer-events": "auto"});
	},function(){
		$(this).children(".tooltip").removeAttr("style");
	});
	/*
	$(".wrapper i").hover(function(){
		$(this).siblings(".tooltip").css({ "opacity": 1, "pointer-events": "auto"});
	},function(){
		$(this).siblings(".tooltip").css({ "opacity": 0, "pointer-events": "auto"});
	});
	*/
	//Upvote.create('runoobvote-id', {callback: vote_callback});
	var ajaxurl = 'https://www.runoob.com/wp-admin/admin-ajax.php';
	var callback = function(data) {
		//console.log($('#runoobvote-id').upvote('upvoted'));
		//console.log($('#runoobvote-id').upvote('downvoted'));
		//console.log(data);
		_vote_action = data.action;
		id_arr = data.id.split('-');
		um_id= id_arr[2];
		//console.log(um_id);
		
		var re = /^[1-9]+/;
		if (re.test(um_id)) { 
			var ajax_data = {
				_vote_action: _vote_action,
				action: "pinglun_zan",
				um_id: um_id,
				um_action: "ding"
			};
			//console.log(ajax_data);
			$.post(ajaxurl,ajax_data,function(status){
				//if(status.vote_num>999) {
				//	_voteHtml = '<span style="display: block; text-align: center;font-size: 20px; color: #6a737c; margin: 8px 0;">'+kFormatter(status.vote_num) +'</span>';
				//	$("#runoobvote-id-" + um_id + " .count").hide().after(_voteHtml);
				//}
				
			});
		}
	};
	if($('#comments').length && $('.upvotejs').length){
		$('.upvotejs').upvote({id: 20470, callback: callback});
	
		$.post(ajaxurl,{"action":"pinglun_zan","postid":20470},function(data){  
			$(data).each(function(key,value) {
				$("#runoobvote-id-" + value.commid + " .upvote").addClass(value.upvotejs_class);
				$("#runoobvote-id-" + value.commid + " .downvote").addClass(value.downvote_class);
				$("#runoobvote-id-" + value.commid + " .count").text(value.upvote_count);
			})
		},'json');
		
	}
	
	
});
function isBlank(str) {
    return (!str || /^\s*$/.test(str));
}
function kFormatter(num) {
	// return num;
    return Math.abs(num) > 999 ? Math.sign(num)*((Math.abs(num)/1000).toFixed(1)) + 'k' : Math.sign(num)*Math.abs(num)
}

</script>

<link rel="stylesheet" href="../0/qa.css?1.44">
<link rel="stylesheet" type="text/css" href="../0/simditor.min.css" />
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script>
			<div class="article-heading-ad" id="w3cnote-ad728">
			<script async src=""></script>
			<!-- 移动版 自动调整 -->
			<ins class="adsbygoogle"
			     style="display:inline-block;min-width:300px;max-width:970px;width:100%;height:90px"
			     data-ad-client="ca-pub-5751451760833794"
			     data-ad-slot="1691338467"
			     data-ad-format="horizontal"></ins>
			<script>
			(adsbygoogle = window.adsbygoogle || []).push({});
			</script>
			</div>
			<style>
@media screen and (max-width: 768px) {
	#w3cnote-ad728 {
		display: none;
	}
}
p.note-author {
    border-bottom: 1px solid #ddd;
    font-size: 18px;
    font-weight: bold;
    color: #78a15a;
    padding-bottom: 2px;
    margin-bottom: 4px;
}
</style>
<script>
var aid = 20470;
</script>
	</div>
		
	</div>
	<div class="listcol last right-column">




<!--
	<div class="tab tab-light-blue"> 订阅</div>
	<div class="sidebar-box">
		<div class="socialicons">
			<a href="../feed" class="rss">RSS 订阅</a>
		
			<form action="//list.qq.com/cgi-bin/qf_compose_send" method="post">
			<input type="hidden" value="qf_booked_feedback" name="t">
			<input type="hidden" value="4b67b6b6c1f5e792559940cab4aebb8f1126fba880bff1a8" name="id">
			<input class="placeholder" id="feed_email" name="to" value="输入邮箱 订阅笔记" autocomplete="off">
			<input type="submit" value="订阅" class="btn btn-primary">
			</form>
		
		</div>
 
	</div>
-->	



<!--
	<div class="sidebar-box cate-list">
	<div class="sidebar-box recommend-here list-link">
			<a href="javascript:void(0);" style="font-size: 16px; color:#64854c;font-weight:bold;">笔记列表</a>
		</div>

 

</div>
-->

	 <div class="sidebar-box cate-list">
		 		

	 	<div class="sidebar-box recommend-here list-link">
			<a href="javascript:void(0);" style="font-size: 16px; color:#64854c;font-weight:bold;">教程列表</a>
		</div>
		
		<div class="cate-items"> 
				<a href="../ado">ADO 教程</a>
	<a href="../ajax">Ajax 教程</a>
	<a href="../android">Android 教程</a>
	<a href="../angularjs2">Angular2 教程</a>
	<a href="../angularjs">AngularJS 教程</a>
	<a href="../appml">AppML 教程</a>
	<a href="../asp">ASP 教程</a>
	<a href="../aspnet">ASP.NET 教程</a>
	<a href="../bootstrap">Bootstrap 教程</a>
	<a href="../bootstrap4">Bootstrap4 教程</a>
	<a href="../bootstrap5">Bootstrap5 教程</a>
	<a href="../cprogramming">C 教程</a>
	<a href="../csharp">C# 教程</a>
	<a href="../cplusplus">C++ 教程</a>
	<a href="../chartjs">Chart.js 教程</a>
	<a href="../cssref">CSS 参考手册</a>
	<a href="../css">CSS 教程</a>
	<a href="../css3">CSS3 教程</a>
	<a href="../django">Django 教程</a>
	<a href="../docker">Docker 教程</a>
	<a href="../dtd">DTD 教程</a>
	<a href="../echarts">ECharts 教程</a>
	<a href="../eclipse">Eclipse 教程</a>
	<a href="../firebug">Firebug 教程</a>
	<a href="../font-awesome">Font Awesome 图标</a>
	<a href="../foundation">Foundation 教程</a>
	<a href="../git">Git 教程</a>
	<a href="../go">Go 语言教程</a>
	<a href="../googleapi">Google 地图 API 教程</a>
	<a href="../highcharts">Highcharts 教程</a>
	<a href="../htmldom">HTML DOM 教程</a>
	<a href="../tags">HTML 参考手册</a>
	<a href="../charsets">HTML 字符集</a>
	<a href="../html">HTML 教程</a>
	<a href="../http">HTTP 教程</a>
	<a href="../ionic">ionic 教程</a>
	<a href="../ios">iOS 教程</a>
	<a href="../java">Java 教程</a>
	<a href="../jsref">JavaScript 参考手册</a>
	<a href="../js">Javascript 教程</a>
	<a href="../jeasyui">jQuery EasyUI 教程</a>
	<a href="../jquerymobile">jQuery Mobile 教程</a>
	<a href="../jqueryui">jQuery UI 教程</a>
	<a href="../jquery">jQuery 教程</a>
	<a href="../json">JSON 教程</a>
	<a href="../jsp">JSP 教程</a>
	<a href="../julia">Julia 教程</a>
	<a href="../kotlin">Kotlin 教程</a>
	<a href="../linux">Linux 教程</a>
	<a href="../lua">Lua 教程</a>
	<a href="../markdown">Markdown 教程</a>
	<a href="../matplotlib">Matplotlib 教程</a>
	<a href="../maven">Maven 教程</a>
	<a href="../memcached">Memcached 教程</a>
	<a href="../mongodb">MongoDB 教程</a>
	<a href="../mysql">MySQL 教程</a>
	<a href="../nodejs">Node.js 教程</a>
	<a href="../numpy">NumPy 教程</a>
	<a href="../pandas">Pandas 教程</a>
	<a href="../perl">Perl 教程</a>
	<a href="../php">PHP 教程</a>
	<a href="../postgresql">PostgreSQL 教程</a>
	<a href="../python3">Python 3 教程</a>
	<a href="../python">Python 基础教程</a>
	<a href="../r">R 教程</a>
	<a href="../rdf">RDF 教程</a>
	<a href="../react">React 教程</a>
	<a href="../redis">Redis 教程</a>
	<a href="../rss">RSS 教程</a>
	<a href="../ruby">Ruby 教程</a>
	<a href="../rust">Rust 教程</a>
	<a href="../sass">Sass 教程</a>
	<a href="../scala">Scala 教程</a>
	<a href="../scipy">SciPy 教程</a>
	<a href="../servlet">Servlet 教程</a>
	<a href="../soap">SOAP 教程</a>
	<a href="../sql">SQL 教程</a>
	<a href="../sqlite">SQLite 教程</a>
	<a href="../svg">SVG 教程</a>
	<a href="../svn">SVN 教程</a>
	<a href="../swift">Swift 教程</a>
	<a href="../tcpip">TCP/IP 教程</a>
	<a href="../typescript">TypeScript 教程</a>
	<a href="../vbscript">VBScript 教程</a>
	<a href="../vue2">Vue.js 教程</a>
	<a href="../vue3">Vue3 教程</a>
	<a href="../w3c">W3C 教程</a>
	<a href="../webservices">Web Service 教程</a>
	<a href="../wsdl">WSDL 教程</a>
	<a href="../xlink">XLink 教程</a>
	<a href="../dom">XML DOM 教程</a>
	<a href="../schema">XML Schema 教程</a>
	<a href="../xml">XML 教程</a>
	<a href="../xpath">XPath 教程</a>
	<a href="../xquery">XQuery 教程</a>
	<a href="../xslfo">XSLFO 教程</a>
	<a href="../xsl">XSLT 教程</a>
	<a href="../data-structures">数据结构</a>
	<a href="../regexp">正则表达式</a>
	<a href="../quiz">测验</a>
	<a href="../browsers">浏览器</a>
	<a href="../quality">网站品质</a>
	<a href="../web">网站建设指南</a>
	<a href="../hosting">网站服务器教程</a>
	<a href="../design-pattern">设计模式</a>
			
		</div> 
		 	 </div>
</div>
	</div>
</div>


<!-- 底部 -->
<div id="footer" class="mar-t50">
   <div class="runoob-block">
    <div class="runoob cf">
     <dl>
      <dt>
       在线实例
      </dt>
      <dd>
       &middot;<a target="_blank" href="../html/html-examples.html">HTML 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../css/css-examples.html">CSS 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../js/js-examples.html">JavaScript 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../ajx/ajax-examples.html">Ajax 实例</a>
      </dd>
       <dd>
       &middot;<a target="_blank" href="../jquery/jquery-examples.html">jQuery 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../xml/xml-examples.html">XML 实例</a>
      </dd>
      <dd>
       &middot;<a target="_blank" href="../java/java-examples.html">Java 实例</a>
      </dd>
     
     </dl>
     <dl>
      <dt>
      字符集&工具
      </dt>
      <dd>
       &middot; <a target="_blank" href="../charsets/html-charsets.html">HTML 字符集设置</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="../tags/html-ascii.html">HTML ASCII 字符集</a>
      </dd>
     <dd>
       &middot; <a target="_blank" href="https://c.runoob.com/front-end/6939/">JS 混淆/加密</a>
      </dd> 
      <dd>
       &middot; <a target="_blank" href="https://c.runoob.com/front-end/6232/">PNG/JPEG 图片压缩</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="../tags/html-colorpicker.html">HTML 拾色器</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="..//c.runoob.com/front-end/53">JSON 格式化工具</a>
      </dd>
      <dd>
       &middot; <a target="_blank" href="..//c.runoob.com/front-end/6680/">随机数生成器</a>
      </dd>
     </dl>
     <dl>
      <dt>
       最新更新
      </dt>
                   <dd>
       &middot;
      <a href="../matplotlib/matplotlib-imread.html" title="Matplotlib imread() 方法">Matplotlib imre...</a>
      </dd>
              <dd>
       &middot;
      <a href="../matplotlib/matplotlib-imsave.html" title="Matplotlib imsave() 方法">Matplotlib imsa...</a>
      </dd>
              <dd>
       &middot;
      <a href="../matplotlib/matplotlib-imshow.html" title="Matplotlib imshow() 方法">Matplotlib imsh...</a>
      </dd>
              <dd>
       &middot;
      <a href="../matplotlib/matplotlib-hist.html" title="Matplotlib 直方图">Matplotlib 直方图</a>
      </dd>
              <dd>
       &middot;
      <a href="../python3/python-func-object.html" title="Python object() 函数">Python object()...</a>
      </dd>
              <dd>
       &middot;
      <a href="../python3/python-ai-draw.html" title="Python AI 绘画">Python AI 绘画</a>
      </dd>
              <dd>
       &middot;
      <a href="../w3cnote/cursor-editor.html" title="神辅助 Cursor 编辑器，加入 GPT-4 让编码更轻松！">神辅助 Cursor ...</a>
      </dd>
             </dl>
     <dl>
      <dt>
       站点信息
      </dt>
      <dd>
       &middot;
       <a target="_blank" href="mailto:admin@runoob.com" rel="external nofollow">意见反馈</a>
       </dd>
      <dd>
       &middot;
      <a target="_blank" href="../disclaimer">免责声明</a>
       </dd>
      <dd>
       &middot;
       <a target="_blank" href="../aboutus">关于我们</a>
       </dd>
      <dd>
       &middot;
      <a target="_blank" href="../archives">文章归档</a>
      </dd>
    
     </dl>
    
     <div class="search-share">
      <div class="app-download">
        <div>
         <strong>关注微信</strong>
        </div>
      </div>
      <div class="share">
      <img width="128" height="128" src="/wp-content/themes/runoob/assets/images/qrcode.png" />
       </div>
     </div>
     
    </div>
   </div>
   <div class="w-1000 copyright">
     Copyright &copy; 2013-2023    <strong><a href="../" target="_blank">菜鸟教程</a></strong>&nbsp;
    <strong><a href="../" target="_blank">runoob.com</a></strong> All Rights Reserved. 备案号：<a target="_blank" rel="nofollow" href="https://beian.miit.gov.cn/">闽ICP备15012807号-1</a>
   </div>
  </div>
  <div class="fixed-btn">
    <a class="go-top" href="javascript:void(0)" title="返回顶部"> <i class="fa fa-angle-up"></i></a>
    <a class="qrcode"  href="javascript:void(0)" title="关注我们"><i class="fa fa-qrcode"></i></a>
    <a class="writer" style="display:none" href="javascript:void(0)"   title="标记/收藏"><i class="fa fa-star" aria-hidden="true"></i></a>
    <!-- qrcode modal -->
    <div id="bottom-qrcode" class="modal panel-modal hide fade in">
      <h4>微信关注</h4>
      <div class="panel-body"><img alt="微信关注" width="128" height="128" src="/wp-content/themes/runoob/assets/images/qrcode.png"></div> 
    </div>
  </div>

 <div style="display:none;">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-84264393-2"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-84264393-2');
</script>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?3eec0b7da6548cf07db3bc477ea905ee";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

</div>
<script>
window.jsui={
    www: 'https://www.runoob.com',
    uri: 'https://www.runoob.com/wp-content/themes/runoob'
};
</script>

<script src=""></script>
<script src=""></script>

</body>
</html>